Home |
| Latest | About | Random
# Week 5 Wednesday Dear Class, Good job on finishing Exam 1! Remember to keep a positive learning attitude, and focus on learning, finding what works and what doesn't, and go from there. Make tiny progress everyday. We'll probably finish grading by next Monday. A "Pre-HW4" is posted, that should build you up to HW4 to come later. No need to turn in Pre-HW4 but it is good to do. Today we start section 10, **references**, and tie up a few loose ends from section 9 about functions. - Matching arguments to parameters - Functions work on **copies** of their arguments when passing by value - Then... **references**! (section 10) - **Passing by value** vs **passing by reference**. - One call, one return value - Function comments - Build error, undefined behavior, or success? A quiz! For your own reading, please go through **9.5** (`main` is a function too!), **9.6** (reviewing syntax) and **9.7** (reviewing function calls) in the text, and try the **review questions in 9.8**. And anything we don't get to cover in class, please read the notes and in the text! -- B. . . . . . ## Who goes where? Last time we had `bool is_triangle(double a, double b, double c)`, and we asked what this does: ```cpp double a = 3; double b = 4; double c = 5; cout << is_triangle(c, a, b); ``` . . . . . Remember, when a function is called, a **function scope** is created, and the **parameters are initialized with the arguments in order**: the first argument goes to the first parameter, the second argument to the second parameter, and so on. **The names do not matter!** So inside the function scope of `is_triangle`, what happens is like ```cpp double a = 5; // first parameter a gets first argument c (which is 5) double b = 3; // second parameter b gets second argument a (which is 3) double c = 4; // third parameter c gets third argument b (which is 4) ``` The `a`, `b`, `c` inside the function are **different variables** from the `a`, `b`, `c` in `main`. They just happen to have the same names. (Draw the dashed box for the function scope, next to the box for `main`!) . . . . . ## Let's write our own swap! But will it swap?? On HW3 you may have used `std::swap` from ``. Let's write our own! ```cpp #include using namespace std; void my_swap(int a, int b); int main() { int a = 6; int b = 7; cout << a << b << endl; my_swap(b, a); cout << a << b << endl; return 0; } void my_swap(int a, int b) { if (a == b) { return; // nothing to do, end the function call early! } int tmp = a; // tmp is a local variable of this function scope a = b; b = tmp; } ``` What is the output? . . . . . ``` 67 67 ``` **Huh?? Nothing got swapped!** Let's trace through what happened when we call `my_swap(b, a)`: - A function scope is created. - The parameters are initialized with the arguments **in order**: like `int a = 7;` (from main's `b`) and `int b = 6;` (from main's `a`). - `a == b` is `false`, so we don't return early. - `tmp` is declared inside the function scope, and the **function's** `a` and `b` are swapped. So now the function's `a` is `6` and the function's `b` is `7`. - We reach the closing brace `}`, the function ends, and the function scope is **destroyed**, with its `a`, `b`, and `tmp`. The `a` and `b` back in `main` were never touched! **With parameters like `int a`, a function works on copies of its arguments.** Changing a parameter does not change the variable you passed in. This is called **passing by value**: only the *value* of the argument is handed to the function, not the variable itself. **Remark.** Notice that `return;` can be used in a `void` function to end the call early. It does not return any value. And if a `void` function reaches its closing brace `}`, there is a hidden `return;` there, so the function ends. **Remark.** `void` is a type whose set of values is empty! So you cannot declare a variable like `void x;`. **Remark.** `std::swap` is also a `void` function, it returns nothing. So writing `a = swap(a, b);` is a **build error**, there is no value to assign to `a`! So... how does `std::swap` manage to change `a` and `b` in `main`, while returning nothing at all?? Let's find out! . . . . . ## References. (`&` "the arrow") **References** are the answer! Inside `main`, we can write ```cpp int i = 0; int& r = i; ``` The type of `r` is **not** `int`, it is `int&`, read aloud as "int ref". It means "a **reference** to an `int`". Recall that a variable name is a label for a storage location in memory (on most machines an `int` takes up 4 bytes and a `double` 8 bytes; a `char` is always 1 byte). A **reference does not store its own data**. Instead, it **refers to data that already exists**. We draw a reference as an **arrow** pointing to the box it refers to. (Draw a box `i` with `0` in it, and an arrow from `r` to that box!) ![[1-teaching/ucla-2026-fall-pic-10a/---drafts/---files/week-5-wednesday-draft 2026-10-28 04.20.56.excalidraw.svg]] %%[[1-teaching/ucla-2026-fall-pic-10a/---drafts/---files/week-5-wednesday-draft 2026-10-28 04.20.56.excalidraw.md|🖋 Edit in Excalidraw]]%% . . . . . ### Rules of references. - **A reference must be initialized** when it is declared. `int& r;` is a **build error**. What would it point to? - **A reference like `int&` must refer to an existing storage location of that type**, like an `int` variable. `int& r = 4;` is a **build error**: the literal `4` has no storage location for `r` to point to. (On Friday we will see references to `const`, like `const int&`, which follow a more relaxed rule.) - **Once initialized, a reference refers to the same storage location forever.** The arrow always starts and ends at the same place. (Kind of like how a `const` variable never changes its value.) So after `int& r = i;`, **any line involving `r` behaves as if we wrote `i` instead.** Follow the arrow! . . . . . ### Follow the arrow! And the arrow never change! What does this print? Predict each line before running it! ```cpp #include using namespace std; int main() { int i = 0; int& r = i; cout << i << ' ' << r << endl; i = 1; cout << i << ' ' << r << endl; r = 2; cout << i << ' ' << r << endl; int j = 3; r = j; // careful! cout << i << ' ' << r << ' ' << j << endl; i = 4; cout << i << ' ' << r << ' ' << j << endl; j = 5; cout << i << ' ' << r << ' ' << j << endl; return 0; } ``` . . . . . ``` 0 0 1 1 2 2 3 3 3 4 4 3 4 4 5 ``` The tricky line is `r = j;`. It does **not** make `r` refer to `j`! The arrow from `r` still points at `i`, so `r = j;` is the same as `i = j;`, which puts `3` into `i`. After that, `j` is its own variable, and changing `j` does nothing to `i` or `r`. . . . . . ### Back to swap! Let's fix our `my_swap`. All we change is **two ampersands**: ```cpp #include using namespace std; void good_swap(int& a, int& b); int main() { int i = 0; int j = 1; cout << i << ' ' << j << endl; good_swap(i, j); cout << i << ' ' << j << endl; return 0; } void good_swap(int& a, int& b) { int tmp = a; a = b; b = tmp; } ``` Output: ``` 0 1 1 0 ``` **It works!** Let's trace through `good_swap(i, j)`: - A function scope is created. - The parameters are initialized with the arguments, in order. But now the parameters are references, so it is like `int& a = i;` and `int& b = j;`. (This is why we say *initialized*, not *assigned*: initializing a reference sets where its arrow points, while assigning to a reference later, like `a = b;` below, writes through the arrow.) (Draw the dashed box, with arrows from `a` to main's `i` and from `b` to main's `j`!) - `int tmp = a;` puts a copy of `i`'s value `0` into `tmp`. Note `tmp` is a plain `int`, its own box. - `a = b;` follows both arrows, so it is like `i = j;`. Now `i` is `1`. - `b = tmp;` follows the arrow, so it is like `j = tmp;`. Now `j` is `0`. - The function ends, and the function scope is destroyed: `a`, `b`, `tmp` are gone. But `a` and `b` never had their own data. **The changes live on in `main`'s `i` and `j`!** This is called **passing by reference**: the parameters refer to the caller's variables themselves, instead of getting copies. Compare with `my_swap`, which **passed by value**. (We will say much more about choosing between the two next week.) **Remark.** `good_swap` is still a procedure, it returns nothing. Its side effect is changing the variables passed in. So with references, a function can "give back" as many results as we like, by changing variables we pass in. **Remark.** `good_swap(0, 1);` is a **build error**. Why? (Hint: look at the rules of references above.) **Remark.** `std::swap` is still better than our `good_swap` in a number of ways, which you will see in PIC 10B and 10C. . . . . . ### Try it! What does this print? ```cpp #include using namespace std; void f(int& a, int b) { ++a; ++b; cout << a << ' ' << b << endl; } int main() { int i = 0; int j = 0; f(i, j); cout << i << ' ' << j << endl; return 0; } ``` Draw the function scope with its arrows! . . . . . Answer: ``` 1 1 1 0 ``` . . . . . **We ended here in class. Please read rest of the following on your own for some loose ends with functions, also please read the book for more details.** ## One call, one return value. Back to functions for a few loose ends. **A single function call never returns two values.** As soon as a `return` is reached, the function call ends. So something like ```cpp int f(int x) { return x; return x + 1; // never reached! } ``` only ever gives back `x`. But with references, we now have a way around this! A function can "give back" extra results by changing variables passed in by reference, just like `good_swap` changed both `i` and `j` without returning anything. . . . . . ## Function comments. From now on, we write a **function comment** for each function we write (other than `main`). It is written inside `/**` and `*/`, and has: - A description of what the function does. - `@param name : description` for each parameter. - `@return description` of what is returned, if the return type is not `void`. It should be enough so someone can use your function **without reading its body**. ```cpp /** Checks whether three side lengths can form a triangle. @param a : the first side length @param b : the second side length @param c : the third side length @return true if a, b, c can form a triangle, false otherwise */ bool is_triangle(double a, double b, double c); ``` Try writing one for `good_swap`! (No `@return` needed, why?) . . . . . ## Build error, undefined behavior, or success? For each program below, is it a **build error**, a **runtime error**, **undefined behavior**, or **success**? Try it and vote! ### (1) ```cpp #include using namespace std; int main() { cout << square(3) << endl; return 0; } double square(double x) { return x * x; } ``` ### (2) ```cpp #include using namespace std; double square(double x); int main() { cout << square(3) << endl; return 0; } ``` ### (3) ```cpp #include using namespace std; double square(double x); double square(double y); double square(double); int main() { cout << square(3) << endl; return 0; } double square(double x) { return x * x; } ``` ### (4) ```cpp #include using namespace std; double square(double x); int main() { cout << square(3) << endl; return 0; } double square(double x) { return x * x; } double square(double x) { return x * x; } ``` ### (5) ```cpp #include using namespace std; bool is_triangle(double a, double b, double c); int main() { bool q = is_triangle(1, 1, 100); cout << q << endl; return 0; } bool is_triangle(double a, double b, double c) { if ((a + b > c) && (a + c > b) && (b + c > a)) { return true; } } ``` . . . . . ### Answers. 1. **Build error.** `square` is called before it is declared. (The definition is after `main`, too late!) 2. **Build error.** `square` is declared but never defined. This is a special kind of build error called a **linking error**: the compiler is happy, but when the pieces get put together, the definition of `square` is nowhere to be found. 3. **Success!** You may **declare** a function as many times as you like, and parameter names in a declaration are optional and don't even need to match. 4. **Build error.** You may only **define** a function once. 5. **Undefined behavior.** Since `1, 1, 100` is not a triangle, the function reaches its closing brace `}` without ever hitting a `return`. If a non-`void` function (other than `main`) does that, the behavior is undefined, even if we never use the returned value. **Make sure every path through a non-`void` function returns something!** (The fix: add `return false;` at the end, or the `else` like we had on Friday.) **Remark.** Here is an even better fix. The condition `(a + b > c) && (a + c > b) && (b + c > a)` is **already** a `bool`, either `true` or `false`. So why not just return it? ```cpp bool is_triangle(double a, double b, double c) { return (a + b > c) && (a + c > b) && (b + c > a); } ``` One line, no `if`, and every path returns something! (Hint: try this on `leap_year` in PHW4.) ---